package com.gaojinqi.lock;

import com.gaojinqi.lock.redis.RedisLock;

import java.util.concurrent.TimeUnit;

/**
 * 分布式锁抽象
 *
 * @author gaojinqi
 * @version 1.0
 * @since 2020年07月07日
 */
public interface IDistributedLock {

    /**
     * 尝试获取锁，返回唯一凭证 <br/>
     * 可自动续租     <br/>
     *
     * @param key        键
     * @param expireTime 超时时间
     * @param timeUnit   时间单位
     * @return 唯一凭证，返回 null 则代表获取锁失败
     */
    boolean tryLock(String key, long expireTime, TimeUnit timeUnit);

    /**
     * 使用凭证解锁
     *
     * @param key 键
     * @return 是否解锁成功
     */
    boolean unlock(String key);

    /**
     * 获取锁，获取不到则会自旋，直到达到最大等待时间为止，返回唯一凭证 <br/>
     * 可自动续租    <br/>
     *
     * @param key          键
     * @param expireTime   超时时间
     * @param timeUnit     时间单位
     * @param maxAwaitTime 最大等待时间，需要大于等于0
     * @return 唯一凭证，返回 null 则代表获取锁失败
     */
    default boolean lock(String key, long expireTime, TimeUnit timeUnit, long maxAwaitTime) {
        if (maxAwaitTime < 0) {
            throw new IllegalArgumentException("最大等待时间必须大于等于0");
        }
        long maxAwaitMills = System.currentTimeMillis() + timeUnit.toMillis(maxAwaitTime);
        while (System.currentTimeMillis() <= maxAwaitMills) {
            // 自旋获取锁
            boolean acquired = tryLock(key, expireTime, timeUnit);
            if (acquired) {
                return acquired;
            }
            try {
                // 强制当前线程放弃剩下的时间片，并休息 1 毫秒
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return false;
    }

    /**
     * 尝试获取锁，返回唯一凭证 <br/>
     * 以秒为单位     <br/>
     * 可自动续租     <br/>
     *
     * @see RedisLock#lock(String, long, TimeUnit, long)
     */
    default boolean lockInSeconds(String key, long expireTime, long maxAwaitTime) {
        return lock(key, expireTime, TimeUnit.SECONDS, maxAwaitTime);
    }

    /**
     * 尝试获取锁，返回唯一凭证 <br/>
     * 以毫秒为单位   <br/>
     * 可自动续租     <br/>
     *
     * @see RedisLock#lock(String, long, TimeUnit, long)
     */
    default boolean lockInMillis(String key, long expireTime, long maxAwaitTime) {
        return lock(key, expireTime, TimeUnit.MILLISECONDS, maxAwaitTime);
    }

    /**
     * 尝试获取锁，返回唯一凭证 <br/>
     * 以秒为单位     <br/>
     * 可自动续租     <br/>
     *
     * @see RedisLock#tryLock(String, long, TimeUnit)
     */
    default boolean tryLockInSeconds(String key, long expireTime) {
        return tryLock(key, expireTime, TimeUnit.SECONDS);
    }

    /**
     * 尝试获取锁，返回唯一凭证 <br/>
     * 以毫秒为单位   <br/>
     * 可自动续租     <br/>
     *
     * @see RedisLock#tryLock(String, long, TimeUnit)
     */
    default boolean tryLockInMillis(String key, long expireTime) {
        return tryLock(key, expireTime, TimeUnit.MILLISECONDS);
    }

}
